package state

import (
	"fmt"
	"strconv"
)

type LuaVM interface {
	LuaState
	PC() int          //返回当前PC
	AddPC(n int)      //修改PC
	Fetch() uint32    //取出指令，并将PC指向下一条指令
	GetConst(idx int) //将指定常量推入栈顶
	GetRK(rk int)     // 将 常量/栈值 推入栈顶
	PeekStack(n int) string

	RegisterCount() int
	LoadVararg(n int)
	LoadProto(bx int)

	CloseUpValues(a int)
}

func (this *luaState) PC() int {
	return this.stack.Pc
}
func (this *luaState) AddPC(n int) {
	this.stack.Pc += n
}
func (this *luaState) Fetch() uint32 {
	c := this.stack.closure.proto.Code[this.stack.Pc]
	this.stack.Pc++
	return c
}
func (this *luaState) GetConst(idx int) {
	c := this.stack.closure.proto.Constants[idx]
	this.stack.push(c)
}
func (this *luaState) GetRK(rk int) {
	if rk > 0xFF {
		this.GetConst(rk & 0xFF)
	} else {
		this.PushValue(rk + 1)
	}
}
func (this *luaState) PeekStack(n int) string {
	v := this.stack.get(n)

	switch x := v.(type) {
	case nil:
		return ""
	case int64, float64:
		return fmt.Sprintf("%v", x)
	case bool:
		return strconv.FormatBool(x)
	case string:
		return x
	case *LuaTable:
		return fmt.Sprintf("{%d}", x.Len())
	case *closure:
		return x.ToStr()
	}
	panic("error PeekStack unknown type")
}

func (this *luaState) RegisterCount() int {
	return int(this.stack.closure.proto.MaxStackSize)
}

func (this *luaState) LoadVararg(n int) {
	if n < 0 {
		n = len(this.stack.varargs)
	}
	this.stack.check(n)
	this.stack.PushN(this.stack.varargs, n)
}

/**
加载子 proto
*/
func (this *luaState) LoadProto(bx int) {
	proto := this.stack.closure.proto.Protos[bx]
	subC := NewLuaClosure(proto)
	this.stack.push(subC)
	//查询外部或栈内的UpVal
	for i, uvInfo := range proto.UpValues {
		idx := int(uvInfo.Idx)
		if uvInfo.Instack == 1 { //当前函数的局部变量,栈上是一定存在的
			if this.stack.openuvs == nil {
				this.stack.openuvs = map[int]*upvalue{}
			}
			if openuv, found := this.stack.openuvs[idx]; found {
				subC.upvalues[i] = openuv
			} else {
				subC.upvalues[i] = &upvalue{&this.stack.slots[idx]}
				this.stack.openuvs[idx] = subC.upvalues[i]
			}
		} else {
			subC.upvalues[i] = this.stack.closure.upvalues[idx]
		}
	}
}

func (this *luaState) CloseUpValues(a int) {
	for i, openuv := range this.stack.openuvs {
		if i >= a-1 {
			//这个是有可能对象保存在栈上，为了避免覆盖，复制出来一份，
			val := *openuv.val
			openuv.val = &val
			delete(this.stack.openuvs, i)
		}
	}
}
